package org.shj.thread.basic;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.TimeUnit;

import org.shj.thread.util.Util;

/**
 * 1. 使用 wait()、notify() 和 notifyAll() 时需要先对调用对象加锁
 * 2. 调用 wait()方法后，线程状态由RUNNING变为 WAITING，并将当前线程放置到对象的等待队列
 * 3. notify() 或 notifyAll() 方法调用后，等待线程依旧不会从 wait()返回，需要调用 notify() 或 notifyALl()的
 *    线程释放锁之后，等待线程才有机会从 wait()返回
 * 4. nofity() 方法将等待队列中的一个等待线程从等待队列中移到同步队列中，而 notifyAll() 方法则是将等待队列中所有
 *    的线程全部移到同步队列，被 移动的线程状态由WAITING 变为 BLOCKED
 * 5. 从 wait()方法返回的前提是获得了调用对象的锁
 * 
 * @author huangjian
 *
 */
public class WaitNotify {
	
	static DateFormat df = new SimpleDateFormat("HH:mm:ss");

	static volatile boolean flag = true;
	static Object lock = new Object();
	
	public static void main(String[] args) throws Exception{
		Thread waitThread = new Thread(new Wait(), "WaitThread");
		waitThread.start();
		
		TimeUnit.SECONDS.sleep(1);
		
		Thread notifyThread = new Thread(new Notify(), "NotifyThread");
		notifyThread.start();
	}
	
	static class Wait implements Runnable{
		public void run(){
			synchronized(lock){ //加锁，拥有lock的 Monitor
				while(flag){
					try{
						System.out.println(Thread.currentThread() + " flag is true. wait @ " + df.format(new Date()));
						lock.wait();
					}catch(InterruptedException e){
						e.printStackTrace();
					}
				}
				
				System.out.println(Thread.currentThread() + " flag is false. Running @ " + df.format(new Date()));
			}
		}
	}
	
	static class Notify implements Runnable{
		public void run(){
			synchronized(lock){				
				//获取lock的锁，然后进行通知，通知时不会释放lock的锁，直到当前线程释放了lock后，WaitThread才能从wait方法中返回
				System.out.println(Thread.currentThread() + " hold lock. notify @ " + df.format(new Date()));
				lock.notifyAll(); // 虽然唤醒了其它线程，但是锁还未被释放
				flag = false;
				Util.sleep(3);
			}

			// 加入下面的一行代码，可以确保上面的 Wait 线程可以先获取锁。注释掉下面的一行代码后，不确定 Wait 线程和后面再次加锁的
			// 那段代码到底哪个会获得锁
			// Util.sleep(2);

			synchronized(lock){//再次加锁
				System.out.println(Thread.currentThread() + " hold lock again. sleep @ " + df.format(new Date()));
				Util.sleep(3);
			}
		}
	}
}
